﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace NNetSed
{
    class Connection
    {
        const int PACKET_SIZE = 16384;

        TcpClient _acceptedClient;
        string _remoteHost;
        int _remotePort;
        bool _disconnect = false;
        static List<string> _searchStrings = new List<string>();
        static List<string> _replaceStrings = new List<string>();
        List<Rule> _rules = new List<Rule>();
        //Transform _transform = new Transform();

        public Connection(TcpClient acceptedClient, string remoteHost, int remotePort, List<Rule> rules)
        {
            _acceptedClient = acceptedClient;
            _remoteHost = remoteHost;
            _remotePort = remotePort;
            _rules = rules;
        }

        public void StartListening()
        {
            //Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
            //clientThread.Start(_acceptedClient);

            HandleConnection(_acceptedClient);
        }

        //--- Private Methods ---

        void HandleConnection(object client)
        {
            TcpClient acceptedClient = (TcpClient)client;
            TcpClient redirectClient = new TcpClient(_remoteHost, _remotePort);

            NetworkStream redirectClientStream = redirectClient.GetStream();
            BeginHandleComm(acceptedClient, redirectClient);
            BeginHandleComm(redirectClient, acceptedClient);
        }

        void BeginHandleComm(TcpClient source, TcpClient destination)
        {
            NetworkStream acceptedClientStream = source.GetStream();

            byte[] message = new byte[PACKET_SIZE];
            int bytesRead;

            bytesRead = 0;

            try
            {
                //blocks until a client sends a message
                acceptedClientStream.BeginRead(message, 0, PACKET_SIZE,
                    (a) =>
                    {
                        bytesRead = source.GetStream().EndRead(a);
                        if (bytesRead == 0)
                        {
                            Disconnect();
                            return;
                        }
                        Debug.Print("Received " + bytesRead + " bytes");

                        int newSize;
                        message = ApplyRules(message, 0, bytesRead, out newSize);

                        destination.GetStream().Write(message, 0, newSize);
                        Debug.Print("Sending " + message.Length + " bytes");

                        if (!IsDisconnected())
                            BeginHandleComm(source, destination);
                    }
                    , destination);
            }
            catch
            {
                //a socket error has occured
            }
        }

        public byte[] ApplyRules(byte[] message, int offset, int size, out int newSize)
        {
            //byte[] test = new byte[size];
            //for (int i = 0; i < size; i++)
            //{
            //    test[i] = message[i];
            //}
            //return test;

            //string asciiMessage = Encoding.UTF8.GetString(message, offset, size);
            string asciiMessage = new string(message.Take(size).Select(Convert.ToChar).ToArray());

            //string newAsciiMessage = _transform.ApplyScript(asciiMessage);

            string newAsciiMessage = asciiMessage;

            foreach (var rule in _rules)
            {
                //newAsciiMessage = newAsciiMessage.Replace(
                //    rule.SearchString,
                //    rule.ReplaceString);
                Regex _regex = new Regex(rule.SearchString, RegexOptions.Multiline);
                _regex.Replace(newAsciiMessage, rule.ReplaceString);
            }

            newAsciiMessage.PadRight(asciiMessage.Length);

            if (true)// || asciiMessage != newAsciiMessage)
            {
                message = newAsciiMessage.Select(Convert.ToByte).ToArray();
                newSize = message.Length;
            }
            //else
                newSize = size;

            return message;
        }


        void Disconnect()
        {
            _disconnect = true;
        }

        bool IsDisconnected()
        {
            return _disconnect;
        }
    }
}
